/*
 *    Copyright © OpenAtom Foundation.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */

package com.inspur.edp.bff.core.manager;

import com.inspur.edp.bef.api.lcp.IResponseContext;
import com.inspur.edp.bef.api.lcp.IStandardLcp;
import com.inspur.edp.bef.core.session.FuncSession;
import com.inspur.edp.bef.core.session.FuncSessionManager;
import com.inspur.edp.bff.api.manager.IFSManagerContext;
import com.inspur.edp.bff.api.manager.SourceType;
import com.inspur.edp.bff.api.manager.assembler.IExtendQueryAssembler;
import com.inspur.edp.bff.api.manager.assembler.IMessageMapperAssembler;
import com.inspur.edp.bff.api.manager.context.ChangeMapperContext;
import com.inspur.edp.bff.api.manager.context.MessageMapperContext;
import com.inspur.edp.bff.spi.AbstractFSAction;
import com.inspur.edp.bff.spi.action.actionstatck.VoActionContext;
import com.inspur.edp.bff.spi.assembler.AbstractAssemblerManager;
import com.inspur.edp.cef.api.RefObject;
import com.inspur.edp.cef.api.message.IBizMessage;
import com.inspur.edp.cef.api.response.ResponseContext;
import com.inspur.edp.cef.core.entityaction.CefActionStack;
import com.inspur.edp.cef.entity.changeset.ChangeDetailMerger;
import com.inspur.edp.cef.entity.changeset.IChangeDetail;
import com.inspur.edp.commonmodel.core.session.distributed.SessionEditToken;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

abstract class ActionExecutor {

    public static <TResult> TResult executeAction(AbstractFSAction<TResult> action, FSManager manager, IFSManagerContext context) {
        SessionEditToken editor = null;
        FuncSession session = FuncSessionManager.getCurrentSession();
        if (session != null) {
            //manager.getBefSessionMgr().beginEdit();
            //TODO:先不加锁, 否则aif生单场景导致死锁
            editor = FuncSessionManager.getCurrent().beginEdit(session, false);
        }
        TResult result;
        try {
            result = action.deal(context);
        } finally {
            if (editor != null)
                FuncSessionManager.getCurrent().endEdit(editor); //manager.getBefSessionMgr().endEdit(editor);
        }
        processResponse(manager, context);
        return result;
    }

    public static <TResult> TResult executeAction(AbstractFSAction<TResult> action, FSManager manager, IFSManagerContext context
            , String actionId, String actionCode, Map<String, Object> parameters, boolean autoSave) {
        pushStatck(action, manager, context, actionId, actionCode, parameters);
        SessionEditToken editor = null;
        FuncSession session = FuncSessionManager.getCurrentSession();
        if (session != null) {
            //manager.getBefSessionMgr().beginEdit();
            //TODO:先不加锁, 否则aif生单场景导致死锁
            editor = FuncSessionManager.getCurrent().beginEdit(session, false);
        }
        try {
            final RefObject<TResult> resultWrapper = new RefObject<>(null);
            Runnable r = () -> {
                resultWrapper.argvalue = action.deal(context);
                processResponse(manager, context);
                if (autoSave)
                    manager.save();
            };
            if (autoSave
                    && manager.getOutSourceConfigs() != null
                    && manager.getOutSourceConfigs().getSourceConfigs() != null
                    && manager.getOutSourceConfigs().getSourceConfigs().size() == 1
                    && manager.getOutSourceConfigs().getSourceConfigs().get(0).getSourceType() == SourceType.Be) {
                IStandardLcp lcp = manager.getBffContext().getLcp();
                lcp.atomicallyInvoke(r);
            } else {
                r.run();
            }

            return resultWrapper.argvalue;
        } finally {
            if (editor != null)
                FuncSessionManager.getCurrent().endEdit(editor); //manager.getBefSessionMgr().endEdit(editor);
            CefActionStack.pop();
        }
    }

    private static <TResult> void pushStatck(AbstractFSAction<TResult> action, FSManager manager, IFSManagerContext context, String actionId, String actionCode, Map<String, Object> parameters) {
        VoActionContext voActionContext = new VoActionContext(context.getVoId(), actionId, actionCode, parameters);
        CefActionStack.push(voActionContext);
    }

    public static void processResponse(FSManager manager, IFSManagerContext context) {
        HashMap<String, IResponseContext> beResponse = new HashMap<String, IResponseContext>();

        for (Map.Entry<String, IStandardLcp> lcp : context.getLcps().entrySet()) {
            if (lcp.getValue() == null || lcp.getValue().getResponseContext() == null) {
                continue;
            }

            beResponse.put(lcp.getKey(), lcp.getValue().getResponseContext());
        }
        //IStandardLcp lcp = context.GetLcp();
        //IResponseContext beResponse = lcp.ResponseContext;
        ResponseContext fsResponse = manager.getResponseContext();
        mergeResponse(fsResponse, beResponse, context);
    }

    private static void mergeResponse(ResponseContext fsResponse, HashMap<String, IResponseContext> beResponse, IFSManagerContext context) {
        mergeChanges(fsResponse, beResponse, context);
        mergeVariableChanges(fsResponse, context);
        mergeMessages(fsResponse, beResponse, context);
        clearBEResponse(beResponse);
    }

    private static void clearBEResponse(HashMap<String, IResponseContext> beResponse) {
        beResponse.entrySet().forEach(item -> item.getValue().clear());
    }

    private static void mergeVariableChanges(ResponseContext fsResponse, IFSManagerContext context) {
        IChangeDetail variableInnerChange = context.getVariableInnerChange();
        if (variableInnerChange == null) {
            return;
        }
        if (fsResponse.getVariableInnerChange() == null) {
            fsResponse.setVariableInnerChange(variableInnerChange.clone());
        } else {
            ChangeDetailMerger.mergeChangeDetail(variableInnerChange, fsResponse.getVariableInnerChange());
        }
    }

    private static void mergeChanges(ResponseContext fsResponse, HashMap<String, IResponseContext> beResponse, IFSManagerContext context) {
        if (beResponse.size() < 1) {
            return;
        }

        HashMap<String, ArrayList<IChangeDetail>> change = new HashMap<>();
        for (Map.Entry<String, IResponseContext> item :
                beResponse.entrySet()) {

            ArrayList<IChangeDetail> changes = new ArrayList<IChangeDetail>(item.getValue().getInnerChangeset().values());
            change.put(item.getKey(), changes);
        }

        ChangeMapperContext mapperContext = new ChangeMapperContext();
        mapperContext.setInnerChange(change);
        context.getAssemblerManager().getChangeMapperAssembler().doChangeMapping2Vo(context, mapperContext);

        mergeChange(fsResponse, mapperContext.getTargetChange());
    }

    private static void mergeChange(ResponseContext fsResponse, ArrayList<IChangeDetail> voChangeDetails) {
        if (voChangeDetails == null || voChangeDetails.isEmpty()) {
            return;
        }

        for (IChangeDetail voChangeDetail : voChangeDetails) {
            //TODO 存在时，是合并还是覆盖
            if (!fsResponse.getInnerChangeset().containsKey(voChangeDetail.getDataID())) {
                fsResponse.getInnerChangeset().put(voChangeDetail.getDataID(), voChangeDetail);
            } else {
                fsResponse.mergeInnerChange(voChangeDetail);
            }
        }
    }

    private static void mergeMessages(ResponseContext fsResponse, HashMap<String, IResponseContext> beResponse, IFSManagerContext context) {
        if (beResponse == null || beResponse.size() < 1) {
            return;
        }

        IMessageMapperAssembler msgMapper = getMesssageMapper(context);
        if (msgMapper != null) {
            HashMap<String, List<IBizMessage>> message = new HashMap<>();
            for (Map.Entry<String, IResponseContext> item : beResponse.entrySet()) {
                if (item.getValue().getMessages() == null || item.getValue().getMessages().isEmpty()) {
                    continue;
                }
                message.put(item.getKey(), new ArrayList<>(item.getValue().getMessages()));
            }

            MessageMapperContext mapperContext = new MessageMapperContext();
            mapperContext.setSourceMessage(message);
            msgMapper.doMessageMapping2Vo(context, mapperContext);
            if (mapperContext.getTargetMessage() != null) {
                fsResponse.addMessage(mapperContext.getTargetMessage());
            }
        } else {
            for (IResponseContext response : beResponse.values()) {
                //改be的message引用
                fsResponse.addMessage(response.getMessages());
            }
        }
    }

    private static IMessageMapperAssembler getMesssageMapper(IFSManagerContext context) {
        if (context.getAssemblerManager() == null || !(context.getAssemblerManager() instanceof AbstractAssemblerManager)) {
            return null;
        }

        return ((AbstractAssemblerManager) context.getAssemblerManager()).getMessageMapperAssembler();
    }

    public abstract IExtendQueryAssembler getAssembler();
}
